package com.joysuch.wwyt.bp.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.google.common.collect.Lists;
import com.joysuch.wwyt.bp.bean.BpInspectPlanSearchBean;
import com.joysuch.wwyt.bp.bean.inspect.*;
import com.joysuch.wwyt.bp.entity.inspect.*;
import com.joysuch.wwyt.bp.entity.inspect.dsl.QBpInspectPlan;
import com.joysuch.wwyt.bp.repository.*;
import com.joysuch.wwyt.bp.service.BPInspectPointTypeService;
import com.joysuch.wwyt.bp.service.BpInspectPlanService;
import com.joysuch.wwyt.config.RedisClient;
import com.joysuch.wwyt.core.bean.SimpleSelectListBean;
import com.joysuch.wwyt.core.common.ajax.ResultBean;
import com.joysuch.wwyt.core.common.util.PageDataUtils;
import com.joysuch.wwyt.core.repository.BaseUserDao;
import com.joysuch.wwyt.core.support.Context;
import com.joysuch.wwyt.util.PageableUtil;
import com.querydsl.core.types.ExpressionUtils;
import javafx.util.Pair;
import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RMapCache;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.IntegerCodec;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import javax.annotation.Resource;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import java.math.BigDecimal;
import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class BpInspectPlanServiceImpl implements BpInspectPlanService {

    @Autowired
    private BpInspectPlanRepository bpInspectPlanRepository;
    @Autowired
    private BpInspectPlanCheckPointRepository bpInspectPlanCheckPointRepository;
    @Autowired
    private BPInspectPointRepository bpInspectPointRepository;
    @Autowired
    private BaseUserDao baseUserDao;
    @Autowired
    private BPInspectPointDetailRepository bpInspectPointDetailRepository;
    @Resource
    private RedisClient redisClient;
    @Autowired
    private BpAppPlanTaskRepository bpAppPlanTaskRepository;
    @Autowired
    private RedissonClient redissonClient;
    @Autowired
    private BpInspectRecordDetailRepository bpInspectRecordDetailRepository;
    @Autowired
    private BpInspectRecordRepository bpInspectRecordRepository;
    @Autowired
    private BpDeviceFacilityCollectionDao bpDeviceFacilityCollectionDao;
    @Autowired
    private BPInspectPointTypeService bpInspectPointTypeService;
    @Autowired
    private BpDepartDao bpDepartDao;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultBean save(BpInspectPlanBean bean) {
        //处理允许误差时间
        String permitDeviationTime = bean.getPermitDeviationTime();
        bean.setPermitDeviationTime(StringUtils.isEmpty(permitDeviationTime) ? null : permitDeviationTime);
        String name = bean.getName();
        BpInspectPlan entity = new BpInspectPlan();
        if (bean.getId() == null) {
            BpInspectPlan temp = bpInspectPlanRepository.findByName(name);
            if (temp != null) {
                return ResultBean.fail(101, "此检查计划名称已存在");
            }
        } else {
            entity = bpInspectPlanRepository.findById(bean.getId()).get();
            if (!name.equals(entity.getName())) {
                int i = bpInspectPlanRepository.countByName(name);
                if (i > 0) {
                    return ResultBean.fail(101, "此检查计划名称已存在");
                }
            }
        }
        BeanUtils.copyProperties(bean, entity);
        entity.setInspectStaffIds(convert(bean.getInspectStaffIds()));
        entity.setInspectDepartIds(convert(bean.getInspectDepartIds()));
        entity.setInspectPositionIds(convert(bean.getInspectPositionIds()));
        entity.setCount(CollectionUtils.isEmpty(bean.getBeanLists()) ? 0 : bean.getBeanLists().size());
        entity.setInspectPeriod(bean.getPeriodSetting());
        if (bean.getId() == null) {
            entity.setVerifyStatus("未通过");
            entity.setInitiateStatus("停用");
        }
        //周期设置格式处理：1-小时-2-次 ->1小时2次
        convertInspectPeriod(entity);
        //计算检查日期，检查时间
        calculateInspectDate(entity);

        BpInspectPlan save = bpInspectPlanRepository.save(entity);
        bpInspectPlanCheckPointRepository.deleteByInspectPlanId(save.getId());
        if (!CollectionUtils.isEmpty(bean.getBeanLists())) {
            for (BpInspectCheckPointBean pointBean : bean.getBeanLists()) {
                BpInspectPlanCheckPoint BpInspectPlanCheckPoint = new BpInspectPlanCheckPoint();
                BeanUtils.copyProperties(pointBean, BpInspectPlanCheckPoint);
                BpInspectPlanCheckPoint.setInspectPlanId(save.getId());
                bpInspectPlanCheckPointRepository.save(BpInspectPlanCheckPoint);
            }
        }
        return ResultBean.defaultSuccessResult();
    }

    /**
     * 每天零点生成当天所有任务
     */
    @Override
    @Scheduled(cron = "${inspect.plan.schedule}")
    public void initAllTask() {
        List<BpInspectPlan> list = bpInspectPlanRepository.findByInitiateStatus("启用");
        List<BpInspectPlan> resultList = Lists.newArrayList();
        for (BpInspectPlan plan : list) {
            try {
                calculateAllDayPlan(resultList, plan);
            } catch (Exception e) {
                log.error("点检任务生成出错，planId:{},errMsg:{}", plan.getId(), e.getMessage());
            }
        }
        produceTaskDetail(resultList);
    }

    @Override
    public ResultBean appGetByPlanId(Long planId) {
        List<BpAppPlanTask> list = bpAppPlanTaskRepository.findByPlanIdAndCheckUserIdAndCheckDateAndStatus(planId,
                Context.getCurrentUserId(), LocalDate.now(), "待完成");
        if (CollectionUtils.isEmpty(list)) {
            return ResultBean.defaultSuccessResult();
        }
        BpInspectPlan inspectPlan = getById(planId);
        List<BpPlanAppTimeDto> checkTimeList = list.parallelStream().map(BpPlanAppTimeDto::new).collect(Collectors.toList());
        inspectPlan.setCheckTimeList(checkTimeList);
        inspectPlan.setCheckTime(checkTimeList.get(0).getCheckTime());
        return ResultBean.success(inspectPlan);
    }

    @Override
    public ResultBean taskQuery(BpInspectTaskQueryBean bean) {
        Pageable pageable = PageRequest.of(bean.getPage(), bean.getSize());
        pageable = PageDataUtils.addOrderByDesc(pageable, "id");
        LocalDate checkDate = bean.getCheckDate();
        String checkUserName = bean.getCheckUserName();
        Long planId = bean.getPlanId();
        String status = bean.getStatus();
        Specification<BpAppPlanTask> query = (Root<BpAppPlanTask> root, CriteriaQuery<?> criteriaQuery, CriteriaBuilder cb) -> {
            List<Predicate> predicates = Lists.newArrayList();
            if (checkDate != null) {
                predicates.add(cb.equal(root.get("checkDate"), checkDate));
            }
            if (checkUserName != null) {
                predicates.add(cb.equal(root.get("checkUserName"), checkUserName));
            }
            if (planId != null) {
                predicates.add(cb.equal(root.get("planId"), planId));
            }
            if (status != null) {
                predicates.add(cb.equal(root.get("status"), status));
            }
            return cb.and(predicates.toArray(new Predicate[predicates.size()]));
        };
        Page<BpAppPlanTask> page = bpAppPlanTaskRepository.findAll(query, pageable);
        List<BpAppPlanTask> planTaskList = page.toList();
        List<Long> planIdList = planTaskList.stream().map(r -> r.getPlanId()).collect(Collectors.toList());
        List<BpInspectPlan> planList = bpInspectPlanRepository.findAllById(planIdList);
        Map<Long, String> planMap = planList.stream().collect(Collectors.toMap(BpInspectPlan::getId, BpInspectPlan::getName));
        List<BpInspectTaskQueryDto> resultList = planTaskList.stream().map(BpInspectTaskQueryDto::new).collect(Collectors.toList());
        for (BpInspectTaskQueryDto dto : resultList) {
            dto.setPlanName(planMap.get(dto.getPlanId()));
        }
        return ResultBean.pageData(resultList, page.getTotalElements());
    }

    /**
     * 获取检查计划列表
     *
     * @param flag
     * @return
     */
    @Override
    public ResultBean getTaskList(String flag) {
        List<SimpleSelectListBean> list = new ArrayList<>();
        List<BpInspectPlan> all = null;
        if (org.apache.commons.lang3.StringUtils.isBlank(flag)) {
            //flag不传 查所有的检查计划
            all = bpInspectPlanRepository.findAll();
        } else {
            //启用;停用
            all = bpInspectPlanRepository.findByInitiateStatus(flag);
        }
        if (!CollectionUtils.isEmpty(all)) {
            for (BpInspectPlan plan : all) {
                SimpleSelectListBean bean = new SimpleSelectListBean();
                bean.setValue(plan.getId());
                bean.setLabel(plan.getName());
                list.add(bean);
            }
        }
        return ResultBean.success(list);
    }

    private void calculateAllDayPlan(List<BpInspectPlan> resultList, BpInspectPlan plan) {
        String periodType = plan.getPeriodType();
        String periodSetting = convertInspectPeriod(plan);
        String periodStartTime = plan.getPeriodStartTime();
        LocalDateTime maxEndTime = LocalDateTime.of(LocalDate.now(), LocalTime.of(23, 59, 59));
        if ("按周期".equals(periodType)) {
            LocalTime temp = LocalTime.parse(periodStartTime, DateTimeFormatter.ofPattern("HH:mm"));
            LocalDateTime tempStartTime = LocalDateTime.of(LocalDate.now(), temp);
            int index = periodSetting.lastIndexOf("次");
            if (periodSetting.contains("小时")) {
                long minuteCount = getMinuteCount(periodSetting, index).getKey();
                LocalTime periodEndTime = plan.getPeriodEndTime();
                Boolean crossDayTask = plan.getCrossDayTask();
                if (crossDayTask) {
                    String planKey = getPlanKey(plan.getTenentId(), plan.getOrgCode(), plan.getId());
                    String s = redisClient.get(planKey);
                    //有缓存生成两个时间段任务
                    if (s != null) {
                        //早半段
                        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                        LocalDateTime morningStartTime = LocalDateTime.parse(s, dateTimeFormatter);
                        produceCrossDayTask(resultList, plan, minuteCount, morningStartTime,
                                Duration.between(morningStartTime, LocalDateTime.of(LocalDate.now(), periodEndTime)));
                        //晚半段
                        produceCrossDayTask(resultList, plan, minuteCount, tempStartTime, Duration.between(tempStartTime, maxEndTime));
                    } else {
                        produceCrossDayTask(resultList, plan, minuteCount, tempStartTime, Duration.between(tempStartTime, maxEndTime));
                    }
                } else {
                    if (periodEndTime != null) {
                        maxEndTime = LocalDateTime.of(LocalDate.now(), periodEndTime);
                    }
                    produceCrossDayTask(resultList, plan, minuteCount, tempStartTime, Duration.between(tempStartTime, maxEndTime));
                }

            } else if (periodSetting.contains("天")) {
                int minuteCount = getMinute(periodSetting, index);
                tempStartTime = setStartTimeFromCache(plan, tempStartTime);
                Duration between = Duration.between(tempStartTime, maxEndTime);
                Long minutes = between.toMinutes();
                long sumTimes = minutes / minuteCount;
                for (int i = 0; i < sumTimes + 1; i++) {
                    LocalDateTime tempTime = tempStartTime.plusMinutes(minuteCount * i);
                    beanCopy(resultList, plan, tempTime.toLocalDate(), tempTime.toLocalTime());
                }
            }
        } else if ("固定时间".equals(periodType)) {
            if ("每天".equals(periodSetting)) {
                beanCopy(resultList, plan, LocalDate.now(), LocalTime.parse(periodStartTime, DateTimeFormatter.ofPattern("HH:mm")));
            } else if ("每周".equals(periodSetting)) {
                String[] split = periodStartTime.split("\\s");
                LocalTime time = LocalTime.parse(split[1], DateTimeFormatter.ofPattern("HH:mm"));
                int nowWeek = LocalDate.now().getDayOfWeek().getValue();
                int needWeek = convertWeek(split[0]);
                if (nowWeek == needWeek) {
                    beanCopy(resultList, plan, LocalDate.now(), time);
                }
            } else if ("每月".equals(periodSetting)) {
                monthOrYearTask(resultList, plan, periodStartTime, true);
            } else if ("每年".equals(periodSetting)) {
                monthOrYearTask(resultList, plan, periodStartTime, false);
            }
        }
    }

    private Pair<Long, BigDecimal> getMinuteCount(String periodSetting, int index) {
        int hourIndex = periodSetting.lastIndexOf("小时");
        String hourString = periodSetting.substring(0, hourIndex);
        String countString = periodSetting.substring(hourIndex + 2, index);
        //多少小时
        int hour = Integer.parseInt(hourString);
        //多少次
        int count = Integer.parseInt(countString);
        BigDecimal divide = BigDecimal.valueOf(hour * 60).divide(BigDecimal.valueOf(count), 0, BigDecimal.ROUND_HALF_DOWN);
        //间隔 --分钟
        if (divide.longValue() == 0 || divide.longValue() < 0) {
            throw new IllegalArgumentException("周期设置不合理,请重新设置");
        }
        return new Pair<>(divide.longValue(), divide);
    }

    private LocalDateTime setStartTimeFromCache(BpInspectPlan plan, LocalDateTime tempStartTime) {
        String planKey = getPlanKey(plan.getTenentId(), plan.getOrgCode(), plan.getId());
        //如果中间新增检查者，将会依据本计划最后生成的任务时间作为起始时间，运行临时生成任务接口将会有问题
        //但是本功能是每天零点生成任务，所以不会出问题
        String s = redisClient.get(planKey);
        if (s != null) {
            DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            tempStartTime = LocalDateTime.parse(s, dateTimeFormatter);
        }
        return tempStartTime;
    }

    private void monthOrYearTask(List<BpInspectPlan> resultList, BpInspectPlan plan,
                                 String periodStartTime, boolean monthTask) {
        String[] split = periodStartTime.split("\\s");
        LocalTime time = LocalTime.parse(split[1], DateTimeFormatter.ofPattern("HH:mm"));
        LocalDate needLocalDate = monthTask ? convertDay(split[0]) : convertMonthAndDay(split[0]);
        if (LocalDate.now().equals(needLocalDate)) {
            beanCopy(resultList, plan, LocalDate.now(), time);
        }
    }

    private void beanCopy(List<BpInspectPlan> resultList, BpInspectPlan plan, LocalDate date, LocalTime time) {
        BpInspectPlan bpInspectPlan = new BpInspectPlan();
        BeanUtils.copyProperties(plan, bpInspectPlan);
        bpInspectPlan.setCheckDate(date);
        bpInspectPlan.setCheckTime(time);
        resultList.add(bpInspectPlan);
    }

    private void produceCrossDayTask(List<BpInspectPlan> resultList, BpInspectPlan plan, long minuteCount, LocalDateTime morningStartTime, Duration between2) {
        Duration duration = between2;
        long minuteDuration = duration.toMinutes();
        long sumTimes = minuteDuration / minuteCount;
        for (int i = 0; i < sumTimes + 1; i++) {
            BpInspectPlan bpInspectPlan = new BpInspectPlan();
            BeanUtils.copyProperties(plan, bpInspectPlan);
            LocalDateTime tempTime = morningStartTime.plusMinutes(minuteCount * i);
            bpInspectPlan.setCheckDate(tempTime.toLocalDate());
            bpInspectPlan.setCheckTime(tempTime.toLocalTime());
            resultList.add(bpInspectPlan);
        }
    }

    /**
     * 每天零点生成当天所有任务;
     */
    private void produceTaskDetail(List<BpInspectPlan> list) {
        List<BpAppPlanTask> needList = Lists.newArrayList();
        for (BpInspectPlan plan : list) {
            LocalDate checkDate = plan.getCheckDate();
            LocalTime checkTime = plan.getCheckTime();
            //过滤时间
            if (!checkDate.isEqual(LocalDate.now())) {
                continue;
            }
            String inspectStaffIds = plan.getInspectStaffIds();
            StaffBean[] bean = getBean(inspectStaffIds);
            if (bean.length == 0) {
                continue;
            }
            Integer taskId = null;
            for (StaffBean staffBean : bean) {
                Long inspectStaffId = staffBean.getInspectStaffId();
                String orgCode = plan.getOrgCode();
                Long tenentId = plan.getTenentId();
                String key = getKey(tenentId, orgCode, plan.getId(), inspectStaffId, checkDate, checkTime);
                String s = redisClient.get(key);
                if (s != null) {
                    continue;
                }
                //新增 进数据库
                if (taskId == null) {
                    taskId = getTaskId();
                }
                String permitDeviationTime = plan.getPermitDeviationTime();
                Integer permitMinute = null;
                if (permitDeviationTime != null) {
                    int minuteIndex = permitDeviationTime.lastIndexOf("分钟");
                    String substring = permitDeviationTime.substring(0, minuteIndex);
                    permitMinute = Integer.parseInt(substring);
                }
                BpAppPlanTask bpAppPlanTask = new BpAppPlanTask(plan.getId(), inspectStaffId, staffBean.getInspectStaffName(),
                        checkDate, checkTime, "待完成", bean.length > 1 ? 1 : 0, taskId, permitMinute);
                bpAppPlanTask.setOrgCode(orgCode);
                bpAppPlanTask.setTenentId(plan.getTenentId());
                needList.add(bpAppPlanTask);
                //新增 进缓存
                redisClient.setEx(key, "exist", 1, TimeUnit.DAYS);
                String planKey = getPlanKey(tenentId, orgCode, plan.getId());
                String format = LocalDateTime.of(checkDate, checkTime).format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
                redisClient.setEx(planKey, format, 5, TimeUnit.DAYS);
            }
        }
        bpAppPlanTaskRepository.saveAll(needList);
    }


    /**
     * 定时检查过期任务
     * app/v2 支持多人任务，去除定时过期判断，由app端上传数据再判断过期与否
     */
    //@Scheduled(initialDelay = 5000, fixedDelay = 20000)
    public void checkTaskStatus() {
        List<BpAppPlanTask> list = bpAppPlanTaskRepository.findByStatus("待完成");
        if (CollectionUtils.isEmpty(list)) {
            return;
        }
        List<BpAppPlanTask> needUpdateList = Lists.newArrayList();
        for (BpAppPlanTask bpAppPlanTask : list) {
            if (bpAppPlanTask.getAcceptTaskUserId() != null) {
                continue;
            }
            LocalDate checkDate = bpAppPlanTask.getCheckDate();
            LocalTime checkTime = bpAppPlanTask.getCheckTime();
            LocalTime now = LocalTime.now();
            Integer permitDeviationTime = bpAppPlanTask.getPermitDeviationTime();
            Duration timeDuration = Duration.between(checkTime, now);
            long l = timeDuration.toMinutes();
            if (checkDate.isBefore(LocalDate.now()) || l > permitDeviationTime) {
                bpAppPlanTask.setStatus("未完成");
                bpAppPlanTask.setStatusDetail("已过期");
                needUpdateList.add(bpAppPlanTask);
            }
        }
        if (CollectionUtils.isEmpty(needUpdateList)) {
            return;
        }
        bpAppPlanTaskRepository.saveAll(needUpdateList);
    }

    public Integer getTaskId() {
        RMapCache<String, Integer> mapCache = redissonClient.getMapCache("nameSpace", IntegerCodec.INSTANCE);
        mapCache.putIfAbsent("idMaker", 0);
        Integer taskId = mapCache.addAndGet("idMaker", 1);
        return taskId;
    }

    private void calculateInspectDate(BpInspectPlan plan) {
        String periodType = plan.getPeriodType();
        String periodSetting = convertInspectPeriod(plan);
        String periodStartTime = plan.getPeriodStartTime();
        if ("按周期".equals(periodType)) {
            LocalDateTime tempStartDateTime;
            LocalTime tempStartTime;
            try {
                tempStartTime = LocalTime.parse(periodStartTime, DateTimeFormatter.ofPattern("HH:mm"));
                tempStartDateTime = LocalDateTime.of(LocalDate.now(), tempStartTime);
            } catch (Exception e) {
                throw new RuntimeException("周期起始时间格式不正确");
            }
            if (!periodSetting.contains("次")) {
                throw new RuntimeException("周期设置格式不正确");
            }
            int index = periodSetting.lastIndexOf("次");
            if (periodSetting.contains("小时")) {
                Pair<Long, BigDecimal> pair = getMinuteCount(periodSetting, index);
                BigDecimal divide = pair.getValue();
                //间隔 --分钟
                long minuteCount = pair.getKey();
                LocalDateTime now = LocalDateTime.now();
                if (now.isAfter(tempStartDateTime)) {
                    Duration duration = Duration.between(tempStartDateTime, now);
                    long minuteDuration = duration.toMinutes();
                    long i = minuteDuration / minuteCount;
                    long mod = minuteDuration % minuteCount;
                    tempStartDateTime = tempStartDateTime.plusMinutes(mod != 0 ? divide.multiply(BigDecimal.valueOf(i + 1)).longValue() :
                            divide.multiply(BigDecimal.valueOf(i)).longValue());
                }
                plan.setMinuteDuration(divide.intValue());
                plan.setCheckTime(tempStartDateTime.toLocalTime());
                plan.setCheckDate(tempStartDateTime.toLocalDate());

                //判断是否隔天
                LocalTime periodEndTime = plan.getPeriodEndTime();
                if (periodEndTime != null) {
                    plan.setCrossDayTask(periodEndTime.isBefore(tempStartTime));
                } else {
                    plan.setCrossDayTask(false);
                }
            } else if (periodSetting.contains("天")) {
                int minute = getMinute(periodSetting, index);
                plan.setMinuteDuration(minute);
                LocalDateTime now = LocalDateTime.now();
                if (plan.getId() != null) {
                    tempStartDateTime = setStartTimeFromCache(plan, tempStartDateTime);
                }
                if (now.isAfter(tempStartDateTime)) {
                    Duration between = Duration.between(tempStartDateTime, now);
                    Long minutes = between.toMinutes();
                    int mod = minutes.intValue() % minute;
                    int i = minutes.intValue() / minute;
                    int plusMinutes = mod != 0 ? (i + 1) * minute : i * minute;
                    tempStartDateTime = tempStartDateTime.plusMinutes(plusMinutes);
                }
                plan.setCheckDate(tempStartDateTime.toLocalDate());
                plan.setCheckTime(tempStartDateTime.toLocalTime());
            } else {
                throw new RuntimeException("周期设置格式不正确");
            }
        } else if ("固定时间".equals(periodType)) {
            if ("每天".equals(periodSetting)) {
                plan.setCheckDate(LocalDate.now());
                plan.setCheckTime(LocalTime.parse(periodStartTime, DateTimeFormatter.ofPattern("HH:mm")));
            } else if ("每周".equals(periodSetting)) {
                String[] split = periodStartTime.split("\\s");
                if (split.length != 2) {
                    throw new RuntimeException("周期起始时间格式不正确");
                } else {
                    LocalTime time = LocalTime.parse(split[1], DateTimeFormatter.ofPattern("HH:mm"));
                    int value = LocalDate.now().getDayOfWeek().getValue();
                    int week = convertWeek(split[0]);
                    LocalDate resultWeek;
                    if (value > week) {
                        resultWeek = LocalDate.now().plusDays(7 - (value - week));
                    } else if (value < week) {
                        resultWeek = LocalDate.now().plusDays(week - value);
                    } else {
                        if (LocalTime.now().isAfter(time)) {
                            resultWeek = LocalDate.now().plusDays(7);
                        } else {
                            resultWeek = LocalDate.now();
                        }
                    }
                    plan.setCheckDate(resultWeek);
                    plan.setCheckTime(time);
                }
            } else if ("每月".equals(periodSetting)) {
                String[] split = periodStartTime.split("\\s");
                if (split.length != 2) {
                    throw new RuntimeException("周期起始时间格式不正确");
                } else {
                    LocalTime time = LocalTime.parse(split[1], DateTimeFormatter.ofPattern("HH:mm"));
                    LocalDate temp = convertDay(split[0]);
                    plan.setCheckDate(temp);
                    plan.setCheckTime(time);
                }
            } else if ("每年".equals(periodSetting)) {
                String[] split = periodStartTime.split("\\s");
                if (split.length != 2) {
                    throw new RuntimeException("周期起始时间格式不正确");
                } else {
                    LocalTime time = LocalTime.parse(split[1], DateTimeFormatter.ofPattern("HH:mm"));
                    LocalDate temp = convertMonthAndDay(split[0]);
                    plan.setCheckDate(temp);
                    plan.setCheckTime(time);
                }
            } else {
                throw new RuntimeException("不支持此周期设置");
            }
        } else {
            throw new RuntimeException("不支持此周期类型");
        }
    }

    private int getMinute(String periodSetting, int index) {
        int dayIndex = periodSetting.lastIndexOf("天");
        String dayString = periodSetting.substring(0, dayIndex);
        String countString = periodSetting.substring(dayIndex + 1, index);
        int day = Integer.parseInt(dayString);
        int count = Integer.parseInt(countString);
        //间隔 --分钟
        int i = day * 24 * 60 / count;
        if (i == 0 || i < 0) {
            throw new IllegalArgumentException("周期设置不合理,请重新设置");
        }
        return i;
    }

    private LocalDate convertMonthAndDay(String monthAndDay) {
        if (StringUtils.isEmpty(monthAndDay.trim()) || !monthAndDay.contains("日")
                || !monthAndDay.contains("月")) {
            throw new RuntimeException("周期起始时间格式不正确");
        } else {
            int monthIndex = monthAndDay.lastIndexOf("月");
            int dayIndex = monthAndDay.lastIndexOf("日");
            String monthString = monthAndDay.substring(0, monthIndex);
            String dayString = monthAndDay.substring(monthIndex + 1, dayIndex);
            try {
                LocalDate target = LocalDate.of(LocalDate.now().getYear(), Integer.parseInt(monthString), Integer.parseInt(dayString));
                LocalDate now = LocalDate.now();
                if (now.isAfter(target)) {
                    return target.plusYears(1);
                } else {
                    return target;
                }
            } catch (NumberFormatException e) {
                throw new RuntimeException("周期起始时间格式不正确");
            }
        }
    }

    private LocalDate convertDay(String day) {
        if (StringUtils.isEmpty(day.trim()) || !day.contains("日")) {
            throw new RuntimeException("周期起始时间格式不正确");
        } else {
            String substring = day.substring(0, day.lastIndexOf("日"));
            try {
                int target = Integer.parseInt(substring);
                int now = LocalDate.now().getDayOfMonth();
                if (target >= now) {
                    return LocalDate.now().plusDays(target - now);
                } else {
                    int maxLength = LocalDate.now().getMonth().maxLength();
                    return LocalDate.now().plusDays(maxLength - (now - target));
                }
            } catch (NumberFormatException e) {
                throw new RuntimeException("周期起始时间格式不正确");
            }
        }
    }

    private int convertWeek(String week) {
        switch (week) {
            case "周一":
                return 1;
            case "周二":
                return 2;
            case "周三":
                return 3;
            case "周四":
                return 4;
            case "周五":
                return 5;
            case "周六":
                return 6;
            case "周日":
                return 7;
            default:
                return 0;
        }
    }

    private String convert(Long[] ids) {
        if (ids == null) {
            return null;
        }
        StringBuilder stringBuilder = new StringBuilder();
        for (Long id : ids) {
            stringBuilder.append(id.toString()).append(",");
        }
        stringBuilder.deleteCharAt(stringBuilder.lastIndexOf(","));
        return stringBuilder.toString();
    }

    private Long[] convert(String ids) {
        if (ids == null) {
            return new Long[]{};
        }
        List<Long> list = new ArrayList<>();
        String[] split = ids.split(",");
        for (String s : split) {
            list.add(Long.parseLong(s));
        }
        return list.toArray(new Long[]{});
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteById(Long id) {
        BpInspectPlan bpInspectPlan = bpInspectPlanRepository.findById(id).orElse(null);
        if (bpInspectPlan == null) {
            throw new IllegalArgumentException("此记录不存在");
        }
        if ("启用".equals(bpInspectPlan.getInitiateStatus())) {
            throw new IllegalArgumentException("启用状态无法删除");
        }
        //如果计划被记录引用，不给删
        List<BpInspectRecord> recordList = bpInspectRecordRepository.findByInspectPlanId(id);
        if (!CollectionUtils.isEmpty(recordList)) {
            throw new IllegalArgumentException("此检查计划已被检查记录绑定，请先解除绑定再删除");
        }
        //如果有待完成的任务不给删
        List<BpAppPlanTask> list = bpAppPlanTaskRepository.findByPlanIdAndStatus(id, "待完成");
        if (!CollectionUtils.isEmpty(list)) {
            throw new IllegalArgumentException("此检查计划有关联的点检任务待完成，请等待任务完成后再删除");
        }
        bpInspectPlanRepository.deleteById(id);
        bpInspectPlanCheckPointRepository.deleteByInspectPlanId(id);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Page<BpInspectPlan> list(Pageable pageable, BpInspectPlanSearchBean searchBean) {
        QBpInspectPlan q = QBpInspectPlan.bpInspectPlan;
        com.querydsl.core.types.Predicate predicate = null;
        if (org.apache.commons.lang3.StringUtils.isNotBlank(searchBean.getNameOrCode())){
            predicate = ExpressionUtils.or(q.id.like("%" + searchBean.getNameOrCode() + "%"),
                                          q.name.like("%" + searchBean.getNameOrCode() + "%"));
        }
        Page<BpInspectPlan> all = predicate == null ? bpInspectPlanRepository.findAll(pageable) : bpInspectPlanRepository.findAll(predicate,pageable);
        for (BpInspectPlan bpInspectPlan : all) {
            setData(bpInspectPlan);
        }
        return all;
    }

    private void setData(BpInspectPlan bpInspectPlan) {
        //通知部门
        Long notifyDepartId = bpInspectPlan.getNotifyDepartId();
        if (notifyDepartId != null) {
            bpInspectPlan.setNotifyDepartName(bpDepartDao.getNameById(notifyDepartId));
        }
        bpInspectPlan.setStaffBeans(getBean(bpInspectPlan.getInspectStaffIds()));
        //产品：暂时只做人员
        //bpInspectPlan.setInspectDepartResultIds(convert(bpInspectPlan.getInspectDepartIds()));
        //bpInspectPlan.setInspectPositionResultIds(convert(bpInspectPlan.getInspectPositionIds()));
        List<BpInspectPlanCheckPoint> list = bpInspectPlanCheckPointRepository.findByInspectPlanId(bpInspectPlan.getId());
        int itemSum = 0;
        for (BpInspectPlanCheckPoint point : list) {
            Long pointId = point.getPointId();
            //检查点名称和类型修改这边要拿最新的
            BPInspectPoint bpInspectPoint = bpInspectPointRepository.findById(pointId).orElse(new BPInspectPoint());
            point.setName(bpInspectPoint.getInspectName());
            point.setInspectLocation(bpInspectPoint.getInspectLocation());
            point.setLocationType(bpInspectPoint.getLocationType());
            Long inspectTypeId = bpInspectPoint.getInspectType();
            point.setType(inspectTypeId != null ? bpInspectPointTypeService.getById(inspectTypeId).getTypeName() : "");

            List<BPInspectPointDetail> details = bpInspectPointDetailRepository.findAllByInspectPointId(pointId);
            details = details.stream().filter(r -> r.getDeviceId() != null).collect(Collectors.toList());
            point.setDeviceCount(details.stream().collect(Collectors.groupingBy(BPInspectPointDetail::getDeviceId)).size());
            int sum = details.stream().map(r -> JSON.parseArray(r.getInspectItem(), String.class).size()).mapToInt(Integer::intValue).sum();
            //项目数
            point.setCount(sum);
            itemSum += sum;

            //添加设备数据
            List<BPInspectPointDetail> pointDetailList = bpInspectPointDetailRepository.findAllByInspectPointId(pointId);
            if (CollectionUtils.isEmpty(pointDetailList)) {
                continue;
            }
            //移除无效数据
            pointDetailList = pointDetailList.stream().filter(r -> r.getDeviceId() != null).collect(Collectors.toList());
            List<BpInspectPlanDeviceDto> deviceDtoList = Lists.newArrayList();
            for (BPInspectPointDetail detail : pointDetailList) {
                List<String> items = JSON.parseArray(detail.getInspectItem(), String.class);
                List<String> standards = JSON.parseArray(detail.getInspectStandard(), String.class);
                if (CollectionUtils.isEmpty(items)) {
                    continue;
                }
                BpInspectPlanDeviceDto dto = new BpInspectPlanDeviceDto();
                dto.setInspectDeviceId(detail.getDeviceId());
                dto.setInspectDeviceName(bpInspectPointDetailRepository.findDeviceNameByDetailId(detail.getId()));

                List<BpInspectPlanDeviceDetailDto> detailDtoList = Lists.newArrayList();
                for (int i = 0; i < items.size(); i++) {
                    BpInspectPlanDeviceDetailDto deviceDetailDto = new BpInspectPlanDeviceDetailDto();
                    String item = items.get(i);
                    deviceDetailDto.setInspectItem(item);
                    deviceDetailDto.setInspectStandard(standards.get(i));
                    detailDtoList.add(deviceDetailDto);
                }
                dto.setDetailDtoList(detailDtoList);
                deviceDtoList.add(dto);
            }
            point.setDeviceDtoList(deviceDtoList);
        }
        bpInspectPlan.setItemSum(itemSum);
        bpInspectPlan.setPointList(list);
        //app/v2 巡检周期
        String inspectPeriod = bpInspectPlan.getInspectPeriod();
        String periodStartTime = bpInspectPlan.getPeriodStartTime();
        if (inspectPeriod != null && inspectPeriod.contains("次")) {
            bpInspectPlan.setAppInspectPeriod(inspectPeriod);
        } else {
            if (periodStartTime != null) {
                String[] split = periodStartTime.split("\\s");
                bpInspectPlan.setAppInspectPeriod((inspectPeriod != null ? inspectPeriod : "") + split[0]);
            }
        }
    }

    //根据人员Id，获取姓名
    private StaffBean[] getBean(String ids) {
        Long[] convert = convert(ids);
        List<StaffBean> staffBeans = Lists.newArrayList();
        for (Long aLong : convert) {
            String name = baseUserDao.getBaseUserNameById(aLong);
            if (!StringUtils.isEmpty(name)) {
                StaffBean staffBean = new StaffBean();
                staffBean.setInspectStaffId(aLong);
                staffBean.setInspectStaffName(name);
                staffBeans.add(staffBean);
            }
        }
        return staffBeans.toArray(new StaffBean[]{});
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public BpInspectPlan getById(Long id) {
        BpInspectPlan bpInspectPlan = bpInspectPlanRepository.findById(id).orElse(null);
        if (bpInspectPlan == null) {
            return null;
        }
        setData(bpInspectPlan);
        return bpInspectPlan;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultBean appGetById(Long id, Long appTaskId) {
        BpInspectPlan bpInspectPlan = getById(id);
        BpAppPlanTask bpAppPlanTask = bpAppPlanTaskRepository.findById(appTaskId).orElse(new BpAppPlanTask());
        bpInspectPlan.setCheckTime(bpAppPlanTask.getCheckTime());
        bpInspectPlan.setCheckDate(bpAppPlanTask.getCheckDate());
        return ResultBean.success(bpInspectPlan);
    }

    @Override
    public ResultBean appNeedDo(Pageable pageable) {
        List<BpAppPlanTask> list = bpAppPlanTaskRepository.findByCheckUserIdAndCheckDateAndStatus(Context.getCurrentUserId(), LocalDate.now(), "待完成");
        if (CollectionUtils.isEmpty(list)) {
            return ResultBean.defaultSuccessResult();
        }
        List<BpInspectPlan> resultList = Lists.newArrayList();
        Map<Long, List<BpAppPlanTask>> map = list.parallelStream().collect(Collectors.groupingBy(BpAppPlanTask::getPlanId));
        List<BpInspectPlan> tempList = bpInspectPlanRepository.findAllById(map.keySet());
        Map<Long, BpInspectPlan> tempMap = tempList.parallelStream().collect(Collectors.toMap(BpInspectPlan::getId, Function.identity()));
        for (Long aLong : map.keySet()) {
            BpInspectPlan inspectPlan;
            if (pageable == null) {
                inspectPlan = tempMap.get(aLong);
                //app/v2 巡检周期
                String inspectPeriod = inspectPlan.getInspectPeriod();
                String periodStartTime = inspectPlan.getPeriodStartTime();
                if (inspectPeriod != null && inspectPeriod.contains("次")) {
                    inspectPlan.setAppInspectPeriod(inspectPeriod);
                } else {
                    if (periodStartTime != null) {
                        String[] split = periodStartTime.split("\\s");
                        inspectPlan.setAppInspectPeriod((inspectPeriod != null ? inspectPeriod : "") + split[0]);
                    }
                }
            } else {
                inspectPlan = getById(aLong);
            }
            List<BpAppPlanTask> planTaskList = map.get(aLong);
            List<BpPlanAppTimeDto> checkTimeList = planTaskList.parallelStream().map(BpPlanAppTimeDto::new).collect(Collectors.toList());
            inspectPlan.setCheckTimeList(checkTimeList);
            inspectPlan.setCheckTime(checkTimeList.get(0).getCheckTime());
            resultList.add(inspectPlan);
        }
        resultList.sort(Comparator.comparing(BpInspectPlan::getCheckTime));
        resultList.forEach(r -> r.setCheckDate(LocalDate.now()));
        if (pageable == null) {
            return ResultBean.success(resultList);
        }
        return ResultBean.success(PageableUtil.getList(pageable, resultList));
    }

    private String convertInspectPeriod(BpInspectPlan bpInspectPlan) {
        String inspectPeriod = bpInspectPlan.getInspectPeriod();
        if (!StringUtils.isEmpty(inspectPeriod) && inspectPeriod.contains("-")) {
            bpInspectPlan.setInspectPeriod(inspectPeriod.replaceAll("-", ""));
            return inspectPeriod.replaceAll("-", "");
        } else {
            return inspectPeriod;
        }
    }

    //flag:  1 启用; 2 停用
    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultBean openOrCloseById(Long id, int flag) {
        BpInspectPlan inspectPlan = bpInspectPlanRepository.findById(id).orElse(null);
        if (inspectPlan == null) {
            return ResultBean.fail(101, "此计划已不存在，请重新尝试");
        }
        if (flag == 1) {
            if ("启用".equals(inspectPlan.getInitiateStatus())) {
                return ResultBean.fail(101, "此计划已处于启用中");
            }
            inspectPlan.setInitiateStatus("启用");
        } else {
            if ("停用".equals(inspectPlan.getInitiateStatus())) {
                return ResultBean.fail(101, "此计划已处于停用中");
            }
            inspectPlan.setInitiateStatus("停用");
        }
        bpInspectPlanRepository.save(inspectPlan);
        return ResultBean.defaultSuccessResult();
    }

    @Override
    public ResultBean checkPath(Long id) {
        BpInspectPlan inspectPlan = bpInspectPlanRepository.findById(id).orElse(null);
        if (inspectPlan == null) {
            return ResultBean.fail(101, "此计划已不存在，请重新尝试");
        }
        List<BpInspectPlanCheckPoint> checkPoints = bpInspectPlanCheckPointRepository.findByInspectPlanId(inspectPlan.getId());
        List<String> list = checkPoints.stream().map(r -> {
            Long pointId = r.getPointId();
            if (pointId != null) {
                BPInspectPoint bpInspectPoint = bpInspectPointRepository.findById(pointId).orElse(null);
                if (bpInspectPoint == null) {
                    log.warn("检查点id:{}已删除", pointId);
                    return null;
                } else {
                    return StringUtils.isEmpty(bpInspectPoint.getInspectLocation().trim()) ?
                            null : bpInspectPoint.getInspectLocation();
                }
            } else {
                return null;
            }
        }).collect(Collectors.toList());
        List<String> resultList = list.stream().filter(r -> r != null).collect(Collectors.toList());
        return ResultBean.success(resultList);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultBean inspectManageNeedFinish(Pageable pageable) {
        Long currentUserId = Context.getCurrentUserId();
        List<BpAppPlanTask> bpAppPlanTasks = bpAppPlanTaskRepository.findByCheckUserIdAndStatus(currentUserId, "待完成");
        if (CollectionUtils.isEmpty(bpAppPlanTasks)) {
            return ResultBean.success(Lists.newArrayList());
        }
        List<BpInspectManageDto> list = bpAppPlanTasks.parallelStream().map(r -> new BpInspectManageDto(r, getById(r.getPlanId()))).collect(Collectors.toList());
        List<BpInspectManageDto> dtoList = list.stream().filter(r -> r.getBpAppPlanTask().getAcceptTaskUserId() == null ||
                r.getBpAppPlanTask().getCheckUserId().equals(r.getBpAppPlanTask().getAcceptTaskUserId())).collect(Collectors.toList());
        dtoList.sort((o1, o2) -> {
            BpAppPlanTask bpAppPlanTask = o1.getBpAppPlanTask();
            BpAppPlanTask bpAppPlanTask1 = o2.getBpAppPlanTask();
            if (!bpAppPlanTask.getCheckDate().isEqual(bpAppPlanTask1.getCheckDate())) {
                return bpAppPlanTask.getCheckDate().compareTo(bpAppPlanTask1.getCheckDate());
            } else {
                return bpAppPlanTask.getCheckTime().compareTo(bpAppPlanTask1.getCheckTime());
            }
        });
        return ResultBean.success(PageableUtil.getList(pageable, dtoList));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultBean inspectManageAlreadyFinish(Pageable pageable) {
        Long currentUserId = Context.getCurrentUserId();
        List<BpAppPlanTask> bpAppPlanTasks = bpAppPlanTaskRepository.findByCheckUserIdAndStatus(currentUserId, "已完成");
        if (CollectionUtils.isEmpty(bpAppPlanTasks)) {
            return ResultBean.success(Lists.newArrayList());
        }
        List<BpInspectManageDto> list = bpAppPlanTasks.parallelStream().map(r -> {
            //已完成的计划需关联记录表Id
            BpInspectPlan plan = getById(r.getPlanId());
            Integer taskId = r.getTaskId();
            LocalDateTime createTime = null;
            LocalDateTime inspectFinishTime = null;
            if (taskId != null) {
                List<BpInspectRecord> recordList = bpInspectRecordRepository.findByInspectUserIdAndAppPlanTaskId(r.getCheckUserId(), r.getId());
                if (!CollectionUtils.isEmpty(recordList)) {
                    BpInspectRecord bpInspectRecord = recordList.get(0);
                    Long recordId = bpInspectRecord.getId();
                    plan.setRecordId(recordId);
                    //计划内部的
                    List<BpInspectRecordDetail> recordDetailList = bpInspectRecordDetailRepository.findByInspectRecordId(recordId);
                    long count = recordDetailList.stream().filter(v -> "异常".equals(v.getInspectResultStatus())).count();
                    plan.setHideDangerSum((int) count);
                    createTime = bpInspectRecord.getCreateTime();
                    inspectFinishTime = bpInspectRecord.getInspectFinishTime();
                }
            }
            return new BpInspectManageDto(r, plan, createTime != null ? createTime : r.getCreateTime(), inspectFinishTime);
        }).collect(Collectors.toList());
        //检查人和接收人同一个人
        List<BpInspectManageDto> dtoList = list.stream().filter(r -> r.getBpAppPlanTask().getCheckUserId().equals(r.getBpAppPlanTask().getAcceptTaskUserId())).collect(Collectors.toList());
        dtoList.sort((o1, o2) -> o2.getCreateTime().compareTo(o1.getCreateTime()));
        return ResultBean.success(PageableUtil.getList(pageable, dtoList));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultBean inspectManageUnFinish(Pageable pageable) {
        Long currentUserId = Context.getCurrentUserId();
        List<BpAppPlanTask> bpAppPlanTasks = bpAppPlanTaskRepository.findByCheckUserIdAndStatus(currentUserId, "未完成");
        if (CollectionUtils.isEmpty(bpAppPlanTasks)) {
            return ResultBean.success(Lists.newArrayList());
        }
        List<BpInspectManageDto> list = bpAppPlanTasks.parallelStream().map(r -> new BpInspectManageDto(r, bpInspectPlanRepository.findById(r.getPlanId()).orElse(new BpInspectPlan()))).collect(Collectors.toList());
        list.sort((o1, o2) -> {
            BpAppPlanTask bpAppPlanTask = o1.getBpAppPlanTask();
            BpAppPlanTask bpAppPlanTask1 = o2.getBpAppPlanTask();
            if (!bpAppPlanTask.getCheckDate().isEqual(bpAppPlanTask1.getCheckDate())) {
                return bpAppPlanTask1.getCheckDate().compareTo(bpAppPlanTask.getCheckDate());
            } else {
                return bpAppPlanTask1.getCheckTime().compareTo(bpAppPlanTask.getCheckTime());
            }
        });
        return ResultBean.success(PageableUtil.getList(pageable, list));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultBean appInit() {
        Long currentUserId = Context.getCurrentUserId();
        String currentOrgCode = Context.getCurrentOrgCode();
        Long currentTenantId = Context.getCurrentTenantId();
        String initKey = getInitKey(currentTenantId, currentOrgCode, currentUserId);
        BpInspectManageDto bpInspectManageDto;
        synchronized (BpInspectPlanServiceImpl.class) {
            String s = redisClient.get(initKey);
            if (s != null) {
                return ResultBean.success(JSONObject.parseObject(s, BpInspectManageDto.class));
            }
            List<BpAppPlanTask> bpAppPlanTasks = bpAppPlanTaskRepository.findByCheckUserIdAndStatus(currentUserId, "待完成");
            if (CollectionUtils.isEmpty(bpAppPlanTasks)) {
                return ResultBean.success(null);
            }
            //当天任务+没人接收
            List<BpAppPlanTask> list = bpAppPlanTasks.stream()
                    .filter(r -> LocalDate.now().isEqual(r.getCheckDate()) && r.getAcceptTaskUserId() == null)
                    .sorted(Comparator.comparing(BpAppPlanTask::getCheckTime))
                    .collect(Collectors.toList());
            if (CollectionUtils.isEmpty(list)) {
                return ResultBean.success(null);
            }
            BpAppPlanTask bpAppPlanTask = list.get(0);
            Long planId = bpAppPlanTask.getPlanId();
            BpInspectPlan bpInspectPlan = bpInspectPlanRepository.findById(planId).orElse(new BpInspectPlan());
            bpInspectManageDto = new BpInspectManageDto(bpAppPlanTask, bpInspectPlan);
            redisClient.setEx(initKey, JSON.toJSONString(bpInspectManageDto), 1, TimeUnit.DAYS);
//            LocalDateTime needTime = LocalDateTime.of(bpAppPlanTask.getCheckDate(), bpAppPlanTask.getCheckTime());
//            String permitDeviationTime = bpInspectPlan.getPermitDeviationTime();
//            String substring = permitDeviationTime.substring(0, permitDeviationTime.length() - 2);
//            int deviationTime = Integer.parseInt(substring);
//            LocalDateTime localDateTime = needTime.plusMinutes(deviationTime);
//            Long appPlanTaskId = bpAppPlanTask.getId();
//            Date date = DateTimeUtil.localDateTime2Date(localDateTime);
//            taskScheduler.schedule(() -> {
//                //当前任务是否期限内被接收,或者被别人接收
//                BpAppPlanTask temp = bpAppPlanTaskRepository.findById(appPlanTaskId).orElse(null);
//                if (temp != null) {
//                    Long acceptTaskUserId = temp.getAcceptTaskUserId();
//                    if (acceptTaskUserId == null || !acceptTaskUserId.equals(currentUserId)) {
//                        log.info("清除过期人员id:{} 没接收的任务，任务id:{},", currentUserId, temp.getId());
//                        redisClient.removeKey(initKey);
//                    }
//                }
//            }, date);
            log.info("app待办事项，人员id:{} 拿到任务，任务表id:{},所属计划id:{}", currentUserId, bpAppPlanTask.getId(), bpInspectPlan.getId());
        }
        return ResultBean.success(bpInspectManageDto);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultBean startCheck(Long id) {
        String currentOrgCode = Context.getCurrentOrgCode();
        Long currentTenantId = Context.getCurrentTenantId();
        Long currentUserId = Context.getCurrentUserId();
        BpAppPlanTask bpAppPlanTask = bpAppPlanTaskRepository.findById(id).orElse(null);
        String initKey = getInitKey(currentTenantId, currentOrgCode, currentUserId);
        if (bpAppPlanTask == null) {
            redisClient.removeKey(initKey);
            return ResultBean.fail(101, "此任务已过期");
        }
        Integer taskId = bpAppPlanTask.getTaskId();
        String taskKey = getTaskKey(currentTenantId, currentOrgCode, taskId);
        synchronized (BpInspectPlanServiceImpl.class) {
            String s = redisClient.get(taskKey);
            //当前接收人按返回又进来
            if (s != null) {
                long userId = Long.parseLong(s);
                if (userId != currentUserId) {
                    redisClient.removeKey(initKey);
                    return ResultBean.fail(101, "此任务已被接下");
                }
            }
            LocalDateTime localDateTime = LocalDateTime.of(bpAppPlanTask.getCheckDate(), bpAppPlanTask.getCheckTime());
            LocalDateTime now = LocalDateTime.now();
            Integer permitDeviationTime = bpAppPlanTask.getPermitDeviationTime();
            LocalDateTime maxDateTime = localDateTime.plusMinutes(permitDeviationTime);
            if (now.isAfter(maxDateTime)) {
                bpAppPlanTask.setStatus("未完成");
                bpAppPlanTask.setStatusDetail("已过期");
                bpAppPlanTaskRepository.save(bpAppPlanTask);
                redisClient.removeKey(initKey);
                return ResultBean.fail(101, "任务已过期");
            }
            List<BpAppPlanTask> planTaskList = bpAppPlanTaskRepository.findByTaskId(taskId);
            planTaskList.forEach(r -> r.setAcceptTaskUserId(currentUserId));
            bpAppPlanTaskRepository.saveAll(planTaskList);
            redisClient.setEx(taskKey, currentUserId.toString(), 1, TimeUnit.DAYS);
        }
        BpInspectPlan plan = getById(bpAppPlanTask.getPlanId());
        log.info("任务appPlanTask id:{} 被人员id:{}抢到,所属计划id:{}", bpAppPlanTask.getId(), currentUserId, plan.getId());
        return ResultBean.success(new BpInspectManageDto(bpAppPlanTask, plan));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultBean relatedOrNot(Long pointId) {
        List<BpInspectPlanCheckPoint> checkPointList = bpInspectPlanCheckPointRepository.findByPointId(pointId);
        if (CollectionUtils.isEmpty(checkPointList)) {
            return ResultBean.success(Lists.newArrayList());
        }
        List<BpInspectPlan> collect = checkPointList.stream().map(r -> getById(r.getInspectPlanId())).collect(Collectors.toList());
        return ResultBean.success(collect.stream().filter(r -> r != null).collect(Collectors.toList()));
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public ResultBean checkFinish(Long appPlanTaskId, Integer uncheckFlag, Long recordId) {
        BpAppPlanTask bpAppPlanTask = bpAppPlanTaskRepository.findById(appPlanTaskId).orElse(new BpAppPlanTask());
        //04-08修改业务逻辑:只要上传，状态即为已完成
        Integer taskId = bpAppPlanTask.getTaskId();
        if (taskId == null) {
            log.warn("任务表id:{} 查到taskId为null", appPlanTaskId);
            return ResultBean.defaultSuccessResult();
        } else {
            //多人任务，都可以做
            if (uncheckFlag == 1) {
                bpAppPlanTask.setStatus("未完成");
            } else {
                bpAppPlanTask.setStatus("已完成");
            }
            bpAppPlanTask.setRecordId(recordId);
            bpAppPlanTaskRepository.save(bpAppPlanTask);
//            List<BpAppPlanTask> planTaskList = Lists.newArrayList();
//            if (bpAppPlanTask.getMultiUserTaskFlag() == 0) {
//                planTaskList.add(bpAppPlanTask);
//            } else {
//                planTaskList.addAll(bpAppPlanTaskRepository.findByTaskId(taskId));
//            }
//            StringBuilder stringBuilder = new StringBuilder();
//            for (BpAppPlanTask appPlanTask : planTaskList) {
//                appPlanTask.setStatus("已完成");
//                stringBuilder.append(appPlanTask.getId()).append(",");
//            }
//            bpAppPlanTaskRepository.saveAll(planTaskList);
            log.info("BpAppPlanTask表数据改状态为已完成,id:" + bpAppPlanTask.getId() + "任务taskId:" + taskId);
        }

        //去除缓存任务
//        Long currentTenantId = Context.getCurrentTenantId();
//        String currentOrgCode = Context.getCurrentOrgCode();
//        Long currentUserId = Context.getCurrentUserId();
//        String taskKey = getTaskKey(currentTenantId, currentOrgCode, taskId);
//        redisClient.removeKey(taskKey);
//        String initKey = getInitKey(currentTenantId, currentOrgCode, currentUserId);
//        redisClient.removeKey(initKey);
//        log.info("app上传检查记录,同时清除task+init缓存，同时修改所有taskId:{} 状态为已完成", taskId);

        //记录表新增具体完成状态详情
        /*Long inspectPlanId = bpInspectRecord.getInspectPlanId();
        BpInspectPlan bpInspectPlan = bpInspectPlanRepository.findById(inspectPlanId).orElse(new BpInspectPlan());
        String checkAllPoint = bpInspectPlan.getCheckAllPoint();
        Integer stopMinTime = bpInspectPlan.getStopMinTime();

        //取出记录表数据
        List<BpInspectRecordPoint> recordPointList = bpInspectRecordPointRepository.findByInspectRecordId(recordId);
        if (CollectionUtils.isEmpty(recordPointList)) {
            return ResultBean.defaultSuccessResult();
        }
        for (BpInspectRecordPoint recordPoint : recordPointList) {
            //判断是否检查全部点位
            if ("是".equals(checkAllPoint)) {
                List<BpInspectRecordDetail> recordDetailList = bpInspectRecordDetailRepository
                        .findByInspectRecordIdAndInspectPlanIdAndInspectPointId(recordId, inspectPlanId, recordPoint.getInspectPointId());
                Long sum = recordDetailList.stream().filter(r -> "未检".equals(r.getInspectResultStatus())).collect(Collectors.counting());
                if (sum > 0) {
                    //记录表
                    bpInspectRecord
                    return ResultBean.defaultSuccessResult();
                }
            }
            //判断是否在指定时间内完成
            if (stopMinTime != null && stopMinTime != 0) {
                Duration duration = Duration.between(recordPoint.getInspectStartTime(), recordPoint.getInspectFinishTime());
                if (duration.toMinutes() < stopMinTime) {
                    Integer taskId = bpAppPlanTask.getTaskId();
                    List<BpAppPlanTask> planTaskList = bpAppPlanTaskRepository.findByTaskId(taskId);
                    for (BpAppPlanTask appPlanTask : planTaskList) {
                        appPlanTask.setStatus("未完成");
                        appPlanTask.setStatusDetail("检查点位时,少于最少停留时间");
                    }
                    bpAppPlanTaskRepository.saveAll(planTaskList);
                    return ResultBean.defaultSuccessResult();
                }
            }
        }
        //正常完成
        Integer taskId = bpAppPlanTask.getTaskId();
        List<BpAppPlanTask> planTaskList = bpAppPlanTaskRepository.findByTaskId(taskId);
        for (BpAppPlanTask appPlanTask : planTaskList) {
            appPlanTask.setStatus("已完成");
        }
        bpAppPlanTaskRepository.saveAll(planTaskList);*/
        return ResultBean.defaultSuccessResult();
    }

    /**
     * 检查计划生成检查任务 去重缓存
     */
    //value: 固定 'exist'
    private String getKey(Long tenentId, String orgCode, Long planId, Long inspectStaffId,
                          LocalDate checkDate, LocalTime checkTime) {
        orgCode = convertOrgCode(orgCode);
        return "plan_task_district:" + tenentId + "_" + orgCode + "_" + planId + "_" + inspectStaffId + "_" + checkDate + "_" + checkTime;
    }

    /**
     * 生成的任务 由谁接受
     * app/v2 弃用
     */
    //value: currentUserId
    private String getTaskKey(Long currentTenantId, String currentOrgCode, Integer taskId) {
        currentOrgCode = convertOrgCode(currentOrgCode);
        return "plan_task:" + currentTenantId + "_" + currentOrgCode + "_" + taskId;
    }

    /**
     * 轮询调用待办事项 缓存key
     */
    //value: bpAppPlanTask表id 改为 BpInspectManageDto
    private String getInitKey(Long currentTenantId, String currentOrgCode, Long currentUserId) {
        currentOrgCode = convertOrgCode(currentOrgCode);
        return "plan_need_do:" + currentTenantId + "_" + currentOrgCode + "_" + currentUserId;
    }

    /**
     * 检查计划对应最后一个时间
     */
    private String getPlanKey(Long currentTenantId, String currentOrgCode, Long planId) {
        currentOrgCode = convertOrgCode(currentOrgCode);
        return "plan_time:" + currentTenantId + "_" + currentOrgCode + "_" + planId;
    }

    private String convertOrgCode(String currentOrgCode) {
        if (currentOrgCode.length() > 7) {
            currentOrgCode = currentOrgCode.substring(0, 7);
        }
        return currentOrgCode;
    }
}
